
package com.jf.cloud.search.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.auth.constant.SysTypeEnum;
import com.jf.cloud.api.dto.EsPageDTO;
import com.jf.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.jf.cloud.api.multishop.vo.ShopDetailVO;
import com.jf.cloud.api.order.bo.EsOrderRefundBO;
import com.jf.cloud.api.order.bo.EsOrderRefundItemBO;
import com.jf.cloud.api.order.constant.BuyerReasonType;
import com.jf.cloud.api.supplier.feign.SupplierDetailFeignClient;
import com.jf.cloud.api.supplier.vo.SupplierApiDetailVO;
import com.jf.cloud.api.vo.EsPageVO;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.i18n.I18nMessage;
import com.jf.cloud.common.i18n.LanguageEnum;
import com.jf.cloud.common.order.constant.OrderActivityType;
import com.jf.cloud.common.order.dto.OrderRefundSearchDTO;
import com.jf.cloud.common.order.vo.EsOrderRefundItemVO;
import com.jf.cloud.common.order.vo.EsOrderRefundVO;
import com.jf.cloud.common.util.Json;
import com.jf.cloud.search.constant.EsConstant;
import com.jf.cloud.search.constant.EsIndexEnum;
import ma.glasnost.orika.MapperFacade;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zz
 * @date 2020/11/16
 */
@Component
public class OrderRefundSearchManager {

    private static final Logger log = LoggerFactory.getLogger(OrderRefundSearchManager.class);

    /** 余额支付（搜索类型） */
    private static final Integer SEARCH_BALANCE = 3;

    /** 1、统计：消费金额 优惠总金额 消费次数 */
    private static final Integer COUNT_TYPE_FIRST = 1;
    /** 2、统计：实付金额 */
    private static final Integer COUNT_TYPE_SECOND = 2;
    /** 3、统计：最近消费时间 */
    private static final Integer COUNT_TYPE_THIRD = 3;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private MapperFacade mapperFacade;

    @Autowired
    private ShopDetailFeignClient shopDetailFeignClient;
    @Autowired
    private SupplierDetailFeignClient supplierDetailFeignClient;


    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderRefundSearchDTO 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsOrderRefundVO> page(EsPageDTO pageDTO, OrderRefundSearchDTO orderRefundSearchDTO) {
        orderRefundSearchDTO.setPageSize(pageDTO.getPageSize());
        orderRefundSearchDTO.setPageNum(pageDTO.getPageNum());
        // 判断处理供应商名称和店铺名称
        Map<Long, String> shopNameMap = checkShopName(orderRefundSearchDTO);
        Map<Long, String> supplierNameMap = checkSupplierName(orderRefundSearchDTO);
        EsPageVO<EsOrderRefundVO> orderRefundPage = pageSearchResult(orderRefundSearchDTO);
        if (CollUtil.isEmpty(orderRefundPage.getList())) {
            return orderRefundPage;
        }
        List<Long> supplierIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<Long> shopIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (EsOrderRefundVO esOrderRefundVO : orderRefundPage.getList()) {
            esOrderRefundVO.setBuyerReasonValue(BuyerReasonType.instance(esOrderRefundVO.getBuyerReason()).getCn());
            supplierIds.add(esOrderRefundVO.getSupplierId());
            shopIds.add(esOrderRefundVO.getShopId());
        }
        // 查询供应商信息
        if (CollUtil.isEmpty(supplierNameMap) && CollUtil.isNotEmpty(supplierIds)) {
            log.info("补充供应商订单消息");
            List<SupplierApiDetailVO> supplierApiDetailVOList = supplierDetailFeignClient.listBySupplierIds(supplierIds).getData();
            supplierNameMap.putAll(supplierApiDetailVOList.stream().collect(Collectors.toMap(SupplierApiDetailVO::getSupplierId, SupplierApiDetailVO::getSupplierName)));
        }
        if (CollUtil.isEmpty(shopNameMap)) {
            log.info("补充店铺订单消息");
            List<ShopDetailVO> shopDetailVOS = shopDetailFeignClient.listByShopIds(shopIds).getData();
            shopNameMap.putAll(shopDetailVOS.stream().collect(Collectors.toMap(ShopDetailVO::getShopId, ShopDetailVO::getShopName)));
        }
        //赠品处理
        for(EsOrderRefundVO esOrderRefundVO : orderRefundPage.getList()){
            esOrderRefundVO.setShopName(null == shopNameMap.get(esOrderRefundVO.getShopId()) ? null : shopNameMap.get(esOrderRefundVO.getShopId()));
            esOrderRefundVO.setSupplierName(null == supplierNameMap.get(esOrderRefundVO.getSupplierId()) ? null : supplierNameMap.get(esOrderRefundVO.getSupplierId()));
            Map<Long, List<EsOrderRefundItemVO>> map = esOrderRefundVO.getOrderItems().stream()
                    .filter(s -> Objects.equals(s.getActivityType(), OrderActivityType.GIVEAWAY.value()))
                    .collect(Collectors.groupingBy(EsOrderRefundItemVO::getActivityId));
            // 如果没有赠品，就不需要处理
            if (MapUtil.isEmpty(map)) {
                continue;
            }
            // 将赠品放到对应的主订单项中
            Iterator<EsOrderRefundItemVO> iterator = esOrderRefundVO.getOrderItems().iterator();
            while (iterator.hasNext()) {
                EsOrderRefundItemVO myOrderItemDto = iterator.next();
                // 移除赠品的订单项，进入下一个循环
                if (Objects.equals(myOrderItemDto.getActivityType(), OrderActivityType.GIVEAWAY.value())) {
                    iterator.remove();
                    continue;
                }
                // 主订单项插入赠品订单项
                if (map.containsKey(myOrderItemDto.getOrderItemId())) {
                    myOrderItemDto.setGiveawayList(map.get(myOrderItemDto.getOrderItemId()));
                }
            }
        }
        return orderRefundPage;
    }

    private Map<Long, String> checkShopName(OrderRefundSearchDTO orderRefundSearchDTO){
        Map<Long, String> shopNameMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (null != orderRefundSearchDTO.getShopName() && !Objects.equals(orderRefundSearchDTO.getShopName(), "")){
            log.info("根据店铺名称查询店铺信息，防止店铺被改名导致的订单数据不准确");
            List<ShopDetailVO> shopList = shopDetailFeignClient.getShopDetailByShopIdAndShopName(orderRefundSearchDTO.getShopName()).getData();
            orderRefundSearchDTO.setShopName(null);
            if (CollUtil.isNotEmpty(shopList)) {
                log.info("将数据库查询的店铺id放入查询条件中");
                shopNameMap.putAll(shopList.stream().collect(Collectors.toMap(ShopDetailVO::getShopId, ShopDetailVO::getShopName)));
                orderRefundSearchDTO.setShopIds(shopList.stream().map(ShopDetailVO::getShopId).collect(Collectors.toList()));
            }else{
                orderRefundSearchDTO.setShopId(Constant.DEFAULT_SHOP_ID);
            }
        }
        return shopNameMap;
    }

    private Map<Long, String> checkSupplierName(OrderRefundSearchDTO orderRefundSearchDTO){
        Map<Long, String> supplierNameMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (null != orderRefundSearchDTO.getSupplierName() && !Objects.equals(orderRefundSearchDTO.getSupplierName(), "")){
            log.info("根据供应商名称查询供应商信息，防止供应商被改名导致的订单数据不准确");
            List<SupplierApiDetailVO> supplierList = supplierDetailFeignClient.getSupplierDetailBySupplierName(orderRefundSearchDTO.getSupplierName()).getData();
            orderRefundSearchDTO.setSupplierName(null);
            if (CollUtil.isNotEmpty(supplierList)) {
                log.info("将数据库查询的店铺id放入查询条件中");
                supplierNameMap.putAll(supplierList.stream().collect(Collectors.toMap(SupplierApiDetailVO::getSupplierId, SupplierApiDetailVO::getSupplierName)));
                orderRefundSearchDTO.setSupplierIds(supplierList.stream().map(SupplierApiDetailVO::getSupplierId).collect(Collectors.toList()));
            }else{
                orderRefundSearchDTO.setSupplierId(Constant.DEFAULT_SUPPLIER_ID);
            }
        }
        return supplierNameMap;
    }


    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderRefundSearchDTO 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsOrderRefundVO> pageSearchResult(OrderRefundSearchDTO orderRefundSearchDTO) {
        if (Objects.isNull(orderRefundSearchDTO.getPageSize())) {
            orderRefundSearchDTO.setPageSize(10);
        }
        if (Objects.isNull(orderRefundSearchDTO.getPageNum())) {
            orderRefundSearchDTO.setPageNum(1);
        }
        //1、查询订单数据
        SearchResponse response = searchResult(orderRefundSearchDTO);
        //2.处理响应的数据
        return pageEsOrderBO(response, orderRefundSearchDTO.getPageSize());
    }

    private EsPageVO<EsOrderRefundVO> pageEsOrderBO(SearchResponse response, Integer pageSize) {
        EsPageVO<EsOrderRefundVO> pageVO = new EsPageVO<>();
        List<EsOrderRefundVO> orderRefundList = getOrderRefundListByResponse(response.getHits().getHits());
        pageVO.setList(orderRefundList);
        buildSearchPage(pageVO, response, pageSize);
        return pageVO;
    }

    /**
     * 构建分页数据
     * @param esPageVO
     * @param response
     * @param pageSize
     */
    private void buildSearchPage(EsPageVO<?> esPageVO, SearchResponse response, Integer pageSize) {
        //总记录数
        long total = response.getHits().getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int)total % pageSize == 0 ?
                (int)total / pageSize : ((int)total / pageSize + 1);
        esPageVO.setPages(totalPages);
    }

    private SearchResponse searchResult(OrderRefundSearchDTO orderRefundSearchDTO) {
        // 封装检索条件
        SearchRequest searchRequest = buildSearchRequest(orderRefundSearchDTO);
        SearchResponse response;
        try {
            //2、执行检索请求
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.info("搜索返回结果：" + response.toString());

        } catch (IOException e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return response;
    }

    private List<EsOrderRefundVO> getEsOrderBOList(SearchResponse response) {
        return getOrderRefundListByResponse(response.getHits().getHits());
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    private List<EsOrderRefundVO> getOrderRefundListByResponse(SearchHit[] hits) {
        List<EsOrderRefundVO> esOrderRefunds = new ArrayList<>();
        Integer lang = I18nMessage.getLang();
        for (SearchHit hit : hits) {
            EsOrderRefundBO esOrderRefundBO = Json.parseObject(hit.getSourceAsString(), EsOrderRefundBO.class);
            for (EsOrderRefundItemBO orderRefundItem : esOrderRefundBO.getOrderItems()) {
                if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
                    orderRefundItem.setSpuName(orderRefundItem.getSpuNameEn());
                    orderRefundItem.setSkuName(orderRefundItem.getSkuNameEn());
                }
                if (StrUtil.isBlank(orderRefundItem.getSpuName())) {
                    orderRefundItem.setSpuName(orderRefundItem.getSpuNameZh());
                }
                if (StrUtil.isBlank(orderRefundItem.getSkuName())) {
                    orderRefundItem.setSkuName(orderRefundItem.getSkuNameZh());
                }
            }
            EsOrderRefundVO orderRefundVO = mapperFacade.map(esOrderRefundBO, EsOrderRefundVO.class);
            esOrderRefunds.add(orderRefundVO);
        }
        return esOrderRefunds;
    }


    /**
     * 准备检索请求
     *
     * @param param   搜索参数
     * @return
     */
    private SearchRequest buildSearchRequest(OrderRefundSearchDTO param) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 指定返回数组中的字段
        if (ArrayUtil.isNotEmpty(param.getFetchSource())) {
            searchSourceBuilder.fetchSource(param.getFetchSource(), null);
        }

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索
        keywordSearch(param, boolQueryBuilder);

        // 排序
        sort(searchSourceBuilder, boolQueryBuilder);

        if (Objects.nonNull(param.getPageNum()) && Objects.nonNull(param.getPageSize())) {
            //分页
            searchSourceBuilder.from((param.getPageNum()-1) * param.getPageSize());
            searchSourceBuilder.size(param.getPageSize());
        }

        log.info("构建的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.ORDER_REFUND.value()}, searchSourceBuilder);
    }


    /**
     * 关键字搜索
     */
    private void keywordSearch(OrderRefundSearchDTO param, BoolQueryBuilder boolQueryBuilder) {

        // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可


        BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery();

        // 店铺名称
        if (StrUtil.isNotBlank(param.getShopName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("shopName", param.getShopName()));
        }

        boolQueryBuilder.must(keywordShouldQuery);
    }

    /**
     * 进行排序
     */
    private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 过滤查询条件，如果有必要的话
     *
     * @param param            查询条件
     * @param boolQueryBuilder 组合进boolQueryBuilder
     */
    private void filterQueryIfNecessary(OrderRefundSearchDTO param, BoolQueryBuilder boolQueryBuilder) {
        // 店铺id
        if (Objects.nonNull(param.getShopId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopId()));
        }
        // 店铺id集合
        if (CollUtil.isNotEmpty(param.getShopIds())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("shopId", param.getShopIds()));
        }
        // 用户id
        if (Objects.nonNull(param.getUserId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("userId", param.getUserId()));
        }
        // 退款id
        if (Objects.nonNull(param.getRefundId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("refundId", param.getRefundId()));
        }
        // 订单id
        if (Objects.nonNull(param.getOrderId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("orderId", param.getOrderId()));
        }
        // 申请类型:1,仅退款,2退款退货
        if (Objects.nonNull(param.getApplyType())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("applyType", param.getApplyType()));
        }

        // 创建时间:  开始时间 - 结束时间
        if (param.getBeginTime() != null || param.getEndTime() != null) {
            String createTime = "createTime";
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(createTime);
            if (param.getBeginTime() != null) {
                rangeQueryBuilder.gte(param.getBeginTime());
            }
            if (param.getEndTime() != null) {
                rangeQueryBuilder.lte(param.getEndTime());
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }


        // 退款状态:(1.买家申请 2.卖家接受 3.买家发货 4.卖家收货 5.退款成功 -1.退款关闭)详情见ReturnMoneyStsType
        if (Objects.nonNull(param.getReturnMoneySts())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("returnMoneySts", param.getReturnMoneySts()));
        }


        // 退款状态列表:(1.买家申请 2.卖家接受 3.买家发货 4.卖家收货 5.退款成功 -1.退款关闭)详情见ReturnMoneyStsType
        if (CollUtil.isNotEmpty(param.getReturnMoneyStsList())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("returnMoneySts", param.getReturnMoneyStsList()));
        }

        // 不等于该退款状态
        if(Objects.nonNull(param.getReturnMoneyStsExclude())){
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery("returnMoneySts", param.getReturnMoneyStsExclude().toString()));
        }

        // 订单类型
        if (Objects.nonNull(param.getOrderType())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("orderType", param.getOrderType()));
        }

        // 退款类型
        if (Objects.nonNull(param.getRefundType())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("refundType", param.getRefundType()));
        }

        // 供应商处理状态， -1.商家自行处理 0.待供应商处理 1.供应商已同意 2.供应商已拒绝
        if (Objects.equals(param.getSysType(), SysTypeEnum.SUPPLIER.value())) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("supplierHandleStatus", -1));
        }

        // 供应商id
        if (Objects.nonNull(param.getSupplierId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("supplierId", param.getSupplierId()));
        }

        // 供应商id集合
        if (CollUtil.isNotEmpty(param.getSupplierIds())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("supplierId", param.getSupplierIds()));
        }


    }

    public SearchRequest buildScrollSearchRequest(OrderRefundSearchDTO param, Scroll scroll) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        //设置最多一次能够取出100笔数据，从第101笔数据开始，将开启滚动查询
        //PS:滚动查询也属于这一次查询，只不过因为一次查不完，分多次查
        searchSourceBuilder.size(EsConstant.MAX_PAGE_SIZE);

        log.info("构建滚动查询的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.ORDER_REFUND.value()}, searchSourceBuilder).scroll(scroll);
    }
}
